
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    
    <title>Common data structures &mdash; Ripyl 1.2 documentation</title>
    
    <link rel="stylesheet" href="../_static/nature.css" type="text/css" />
    <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
    <link rel="stylesheet" href="../_static/ripyl.css" type="text/css" />
    
    <script type="text/javascript">
      var DOCUMENTATION_OPTIONS = {
        URL_ROOT:    '../',
        VERSION:     '1.2',
        COLLAPSE_INDEX: false,
        FILE_SUFFIX: '.html',
        HAS_SOURCE:  true
      };
    </script>
    <script type="text/javascript" src="../_static/jquery.js"></script>
    <script type="text/javascript" src="../_static/underscore.js"></script>
    <script type="text/javascript" src="../_static/doctools.js"></script>
    <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
    <link rel="top" title="Ripyl 1.2 documentation" href="../index.html" />
    <link rel="next" title="Ripyl protocols" href="protocols.html" />
    <link rel="prev" title="Reading oscilloscope data" href="reading_data.html" /> 
  </head>
  <body>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="../genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="../py-modindex.html" title="Python Module Index"
             >modules</a> |</li>
        <li class="right" >
          <a href="protocols.html" title="Ripyl protocols"
             accesskey="N">next</a> |</li>
        <li class="right" >
          <a href="reading_data.html" title="Reading oscilloscope data"
             accesskey="P">previous</a> |</li>
        <li><a href="../index.html">Ripyl 1.2 documentation</a> &raquo;</li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body">
            
  <div class="section" id="common-data-structures">
<h1>Common data structures<a class="headerlink" href="#common-data-structures" title="Permalink to this headline">¶</a></h1>
<p>The Ripyl library makes use of a few common data structures that will be discussed below.</p>
<div class="section" id="streams">
<span id="id1"></span><h2>Streams<a class="headerlink" href="#streams" title="Permalink to this headline">¶</a></h2>
<p>Throughout the Ripyl documentation there is reference to &#8220;streams&#8221; of data. This is a term used to describe a sequence of sample or edge data that is used as input and output of many library functions. A stream is a sequence of data that may be produced on demand by an iterator or generator function. Sample streams and edge streams are the two types of stream data structures used in Ripyl. Internally, all of the decode functions convert their input streams to edge streams before proceding to decode their content.</p>
<div class="section" id="sample-streams">
<h3>Sample streams<a class="headerlink" href="#sample-streams" title="Permalink to this headline">¶</a></h3>
<p>Sample streams are farily self explanatory. They are a time series of sampled data points. For efficiency purposes, a group of samples is aggregated into a <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.SampleChunk" title="ripyl.streaming.SampleChunk"><tt class="xref py py-class docutils literal"><span class="pre">SampleChunk</span></tt></a> object containing a NumPy array of samples and attributes identifying the start time and sample period of each chunk. The number of samples in a chunk may vary but it defaults to 10000. Raw sample data can be converted to a sample stream with the <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.samples_to_sample_stream" title="ripyl.streaming.samples_to_sample_stream"><tt class="xref py py-func docutils literal"><span class="pre">samples_to_sample_stream()</span></tt></a> function. A sample stream can be converted back into a raw array of samples with <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.sample_stream_to_samples" title="ripyl.streaming.sample_stream_to_samples"><tt class="xref py py-func docutils literal"><span class="pre">sample_stream_to_samples()</span></tt></a>. The <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.extract_all_samples" title="ripyl.streaming.extract_all_samples"><tt class="xref py py-func docutils literal"><span class="pre">extract_all_samples()</span></tt></a> function will produce a raw array along with information about the start time and sample period.</p>
</div>
<div class="section" id="edge-streams">
<h3>Edge streams<a class="headerlink" href="#edge-streams" title="Permalink to this headline">¶</a></h3>
<p>Edge streams are different from sample streams in that they represent the logical levels of edge transitions. An edge stream consists of a sequence (list, tuple, etc.) or iterable (iterator or generator function object) that contains/yields a series of tuple pairs of numbers. The first number is a float representing the time that the second element of the pair occurs at. The second number is an int that represents the logical edge state (high, low, middle, etc.).</p>
<p>The time value in each pair is an arbitrary time in seconds relative to whatever point is desired. Negative values are acceptable. The only firm requirement is that time increases monotonically. The time intervals between edges are not fixed. The logical state of each edge represents a transition at the current time that is maintained until the next edge in the sequence. Logical states are encoded as an integer value. For waveforms with binary states these will be 0 for low and 1 for high. For differential signals the states are -1 for low, 0 for differential-0, and 1 for high. The first element of an edge stream establishes the initial state of the stream and does not represent an edge.</p>
<p>Edge streams can be manually created when necessary. They can also be created from a sample stream using the <a class="reference internal" href="../apidoc/ripyl.html#ripyl.decode.find_edges" title="ripyl.decode.find_edges"><tt class="xref py py-func docutils literal"><span class="pre">find_edges()</span></tt></a> and <a class="reference internal" href="../apidoc/ripyl.html#ripyl.decode.find_multi_edges" title="ripyl.decode.find_multi_edges"><tt class="xref py py-func docutils literal"><span class="pre">find_multi_edges()</span></tt></a> functions. An edge stream can be converted back to a sample stream using <a class="reference internal" href="../apidoc/ripyl.html#ripyl.sigproc.edges_to_sample_stream" title="ripyl.sigproc.edges_to_sample_stream"><tt class="xref py py-func docutils literal"><span class="pre">edges_to_sample_stream()</span></tt></a> and <a class="reference internal" href="../apidoc/ripyl.html#ripyl.sigproc.synth_wave" title="ripyl.sigproc.synth_wave"><tt class="xref py py-func docutils literal"><span class="pre">synth_wave()</span></tt></a>.</p>
</div>
</div>
<div class="section" id="streamrecords">
<h2>StreamRecords<a class="headerlink" href="#streamrecords" title="Permalink to this headline">¶</a></h2>
<p>All of the base-level decode functions that operate on raw sampled data produce output in the form of an iterable yielding objects based on the <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.StreamRecord" title="ripyl.streaming.StreamRecord"><tt class="xref py py-class docutils literal"><span class="pre">StreamRecord</span></tt></a> class. This allows for the simple implementation of higher level protocols that consume base-level decoder output and yield their own StreamRecord derived objects. The iterators producing StreamRecord objects are also referred to as &#8220;streams&#8221; in the documentation. The distinction between the these and the sample/edge streams is apparent from the context of processing that is respectively performed after or before decoding.</p>
<p>All StreamRecord objects have four main attributes: <tt class="xref py py-attr docutils literal"><span class="pre">kind</span></tt>, <tt class="xref py py-attr docutils literal"><span class="pre">status</span></tt>,
<tt class="xref py py-attr docutils literal"><span class="pre">subrecords</span></tt>, and <tt class="xref py py-attr docutils literal"><span class="pre">stream_id</span></tt>.</p>
<p>The <tt class="docutils literal"><span class="pre">kind</span></tt> attribute is a string that provides a way to identify different types of StreamRecord objects. This allows a protocol to return different &#8216;kinds&#8217; of data without necessarily creating different sub-classes for each one.</p>
<p>The <tt class="docutils literal"><span class="pre">status</span></tt> attribute is an integer code representing the general status of the decode process for each StreamRecord. This provides a way to report errors without interrupting subsequent processing. The baseline status codes are defined in the enumeration <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.StreamStatus" title="ripyl.streaming.StreamStatus"><tt class="xref py py-class docutils literal"><span class="pre">ripyl.streaming.StreamStatus</span></tt></a>. The default success code is &#8220;Ok&#8221; which is 0. Any status code above &#8220;Warning&#8221; (100) is a warning and any code above &#8220;Error&#8221; (200) is an error. Additional status codes may be defined by each protocol.</p>
<p>The <tt class="docutils literal"><span class="pre">subrecords</span></tt> attribute is a list of additional StreamRecord objects that are the children of the current object. They are used by various decoders to create a heirarchy of decoded data at varying levels of detail. An example case is the <a class="reference internal" href="../apidoc/ripyl.protocol.html#module-ripyl.protocol.uart" title="ripyl.protocol.uart"><tt class="xref py py-mod docutils literal"><span class="pre">UART</span></tt></a> decoder that yields StreamRecords for each decoded byte each of which has subrecords with details on the start bit, parity bit, and stop bit locations.</p>
<p>The <tt class="docutils literal"><span class="pre">stream_id</span></tt> attribute is largely unused in the current implementation of Ripyl. It is intended to allow separate streams of decoded data to be present in a single iterator. Each stream is assigned a different ID number that can be checked later to isolate data from different streams. The <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.merge_streams" title="ripyl.streaming.merge_streams"><tt class="xref py py-func docutils literal"><span class="pre">merge_streams()</span></tt></a> function combines two separate StreamRecord streams and assigns new IDs to each one. There is no practical use for this behavior as yet, though.</p>
<p>StreamRecord objects have a <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.StreamRecord.nested_status" title="ripyl.streaming.StreamRecord.nested_status"><tt class="xref py py-meth docutils literal"><span class="pre">nested_status()</span></tt></a> method that returns the largest status code for the current StreamRecord and all of its children. This can be useful when an error code is present in a subrecord but not in the containing StreamRecord.</p>
<div class="section" id="annotation">
<h3>Annotation<a class="headerlink" href="#annotation" title="Permalink to this headline">¶</a></h3>
<p>StreamRecord objects have additional attributes used to support plot annotation. These are <tt class="docutils literal"><span class="pre">style</span></tt>, <tt class="docutils literal"><span class="pre">data_format</span></tt>, and <tt class="docutils literal"><span class="pre">fields</span></tt>. The <tt class="docutils literal"><span class="pre">style</span></tt> attribute is a string identifying the name of a style defined in ripyl.util.plot.annotation_styles. <tt class="docutils literal"><span class="pre">data_format</span></tt> is an <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.AnnotationFormat" title="ripyl.streaming.AnnotationFormat"><tt class="xref py py-class docutils literal"><span class="pre">AnnotationFormat</span></tt></a> value identifying the format of a text label for the record. <tt class="docutils literal"><span class="pre">fields</span></tt> is a dict containing additional kay, value pairs of useful display information. These attributes can be set together with the <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.StreamRecord.annotate" title="ripyl.streaming.StreamRecord.annotate"><tt class="xref py py-meth docutils literal"><span class="pre">annotate()</span></tt></a> method.</p>
</div>
<div class="section" id="streamrecord-subclasses">
<h3>StreamRecord subclasses<a class="headerlink" href="#streamrecord-subclasses" title="Permalink to this headline">¶</a></h3>
<p>There are two main sub-classes of StreamRecord: <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.StreamSegment" title="ripyl.streaming.StreamSegment"><tt class="xref py py-class docutils literal"><span class="pre">StreamSegment</span></tt></a> and <a class="reference internal" href="../apidoc/ripyl.html#ripyl.streaming.StreamEvent" title="ripyl.streaming.StreamEvent"><tt class="xref py py-class docutils literal"><span class="pre">StreamEvent</span></tt></a>. The former represents information extracted from a span of time in the input stream. The latter represents events that happen at a specific point in time. StreamSegments can overlap in time. The children of a StreamSegment will typically be other StreamSegment objects that have a time span contained within the bounds of their parent but this is not rigidly enforced by the Ripyl library.</p>
<p>StreamSegment and StreamEvent add a <tt class="docutils literal"><span class="pre">data</span></tt> attribute to the base StreamRecord. This is the location of any decoded data represented by the object. Its type is dependent on the decoder. Some decoders store a plain integer representing a decoded byte or word. Other decoders will put more complex objects into the <tt class="docutils literal"><span class="pre">data</span></tt> attribute thus using the StreamSegment as a wrapper for insertion into the output stream. The attribute may be None if there is nothing useful to be stored.</p>
<p>StreamSegment objects have <tt class="docutils literal"><span class="pre">start_time</span></tt> and <tt class="docutils literal"><span class="pre">end_time</span></tt> attributes representing the span of time they cover. StreamEvent objects have a <tt class="docutils literal"><span class="pre">time</span></tt> attribute to identify the time of their event.</p>
<p>Each protocol decoder has its own system for representing decoded data in the StreamRecord-based objects. They generally sub-class StreamSegment and may have additional methods and attributes added to the base object. In addition to any sub-classing, StreamRecord objects can always be differentiated by their <tt class="docutils literal"><span class="pre">kind</span></tt> attributes.</p>
</div>
</div>
<div class="section" id="iterators">
<h2>Iterators<a class="headerlink" href="#iterators" title="Permalink to this headline">¶</a></h2>
<p>Many of the functions in Ripyl are <a class="reference external" href="http://docs.python.org/2/howto/functional.html#generators">generator functions</a> that yield results through an iterable generator object rather than returning a result all at once. Some functions require an iterator as input and will not work properly if a list is passed instead. The following examples show how to convert between lists and iterators as needed.</p>
<p>It is important to realize that generator objects result in lazy evaluation and that the function call to them does not terminate until they have no more data to produce. You can force complete evaluation of a generator with the list() built-in.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="c"># Decode function produces an iterable generator object</span>
<span class="n">records_it</span> <span class="o">=</span> <span class="n">XXX</span><span class="o">.</span><span class="n">XXX_decode</span><span class="p">()</span>

<span class="c"># The decode operation has *not* been performed yet</span>

<span class="n">records</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">records_it</span><span class="p">)</span>
<span class="c"># The list() built-in consumes the iterator and forces execution of XXX_decode()</span>
</pre></div>
</div>
<p>Note that iterators can only advance through a sequence and once completed they can not be reiterated again. If you need to feed the data from a consumed iterator back into a function you should save it as a list object and then use iter() to create a fresh iterator over that list.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="c"># The SPI simulator produces three edge stream iterators in a tuple</span>
<span class="n">clk_it</span><span class="p">,</span> <span class="n">data_io_it</span><span class="p">,</span> <span class="n">cs_it</span> <span class="o">=</span> <span class="n">spi</span><span class="o">.</span><span class="n">spi_synth</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>

<span class="c"># Convert the edge stream to a sample stream</span>
<span class="n">clk_ss_it</span> <span class="o">=</span> <span class="n">sigproc</span><span class="o">.</span><span class="n">synth_wave</span><span class="p">(</span><span class="n">clk_it</span><span class="p">,</span> <span class="n">sample_rate</span><span class="p">,</span> <span class="n">rise_time</span><span class="p">)</span>

<span class="c"># clk_it can no longer be used by another function as it is being consumed by synth_wave()</span>

<span class="c"># Consume the sample iterator</span>
<span class="n">clk_samples</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">clk_ss_it</span><span class="p">)</span>

<span class="c"># clk_ss_it can no longer be used by another function</span>

<span class="c"># Create a new iterator on clk_samples using iter()</span>
<span class="n">records_it</span> <span class="o">=</span> <span class="n">spi</span><span class="o">.</span><span class="n">spi_decode</span><span class="p">(</span><span class="nb">iter</span><span class="p">(</span><span class="n">clk_samples</span><span class="p">),</span> <span class="o">...</span><span class="p">)</span>
</pre></div>
</div>
<p>You can also use the built-in <a class="reference external" href="http://docs.python.org/2/library/itertools.html#itertools.tee">itertools.tee()</a> function to split an iterator into two or more iterators if you need to process a stream data set more than once. In this example the clk_ss_it variable is repeatedly rebound to new iterator objects but the previous iterators continue to exist until the entire data set is consumed.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">itertools</span>
<span class="o">...</span>

<span class="c"># Tee the sample iterator (nothing consumed yet)</span>
<span class="n">clk_samples</span><span class="p">,</span> <span class="n">clk_ss_it</span> <span class="o">=</span> <span class="n">itertools</span><span class="o">.</span><span class="n">tee</span><span class="p">(</span><span class="n">clk_ss_it</span><span class="p">)</span>

<span class="c"># clk_ss_it has been reassigned to a new iterator and clk_samples</span>
<span class="c"># is now also an iterator</span>

<span class="c"># We can use clk_samples directly now. clk_samples is consumed here</span>
<span class="n">records_it</span> <span class="o">=</span> <span class="n">spi</span><span class="o">.</span><span class="n">spi_decode</span><span class="p">(</span><span class="n">clk_samples</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span>

<span class="c"># clk_ss_it is still iterable after clk_samples has been consumed</span>
<span class="k">for</span> <span class="n">t</span><span class="p">,</span><span class="n">s</span> <span class="ow">in</span> <span class="n">clk_ss_it</span><span class="p">:</span>
    <span class="k">pass</span>
</pre></div>
</div>
<p>The functions in the <a class="reference internal" href="../apidoc/ripyl.html#module-ripyl.sigproc" title="ripyl.sigproc"><tt class="xref py py-mod docutils literal"><span class="pre">sigproc</span></tt></a> module have been designed to take an iterable stream as input and yield a stream as output. This allows them to be chained without generating intermediate lists of data. See the section on <a class="reference internal" href="simulation.html#signal-processing"><em>signal processing</em></a> for more information.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">ripyl.sigproc</span> <span class="kn">as</span> <span class="nn">sp</span>
<span class="o">...</span>

<span class="n">clk_ss_it</span> <span class="o">=</span> <span class="n">sp</span><span class="o">.</span><span class="n">synth_wave</span><span class="p">(</span><span class="n">clk_it</span><span class="p">,</span> <span class="n">sample_rate</span><span class="p">,</span> <span class="n">rise_time</span><span class="p">)</span>
<span class="n">clk_ss_it</span> <span class="o">=</span> <span class="n">sp</span><span class="o">.</span><span class="n">amplify</span><span class="p">(</span><span class="n">clk_ss_it</span><span class="p">,</span> <span class="n">gain</span><span class="o">=</span><span class="mf">10.0</span><span class="p">,</span> <span class="n">offset</span><span class="o">=</span><span class="mf">5.0</span><span class="p">)</span>
<span class="n">clk_ss_it</span> <span class="o">=</span> <span class="n">sp</span><span class="o">.</span><span class="n">noisify</span><span class="p">(</span><span class="n">clk_ss_it</span><span class="p">,</span> <span class="n">snr_db</span><span class="o">=</span><span class="mf">20.0</span><span class="p">)</span>
<span class="n">clk_ss_it</span> <span class="o">=</span> <span class="n">sp</span><span class="o">.</span><span class="n">quantize</span><span class="p">(</span><span class="n">clk_ss_it</span><span class="p">,</span> <span class="n">full_scale</span><span class="o">=</span><span class="mf">10.0</span><span class="p">)</span>
<span class="c"># No proecssing performed up to this point</span>

<span class="c"># Consume iterator and perform all previous operations</span>
<span class="n">clk_samples</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">clk_ss_it</span><span class="p">)</span>
</pre></div>
</div>
<p>An operation chain can also be performed as nested function calls. This becomes impractical, however, for more than a couple operations.</p>
<div class="highlight-python"><div class="highlight"><pre><span class="kn">import</span> <span class="nn">ripyl.sigproc</span> <span class="kn">as</span> <span class="nn">sp</span>
<span class="o">...</span>

<span class="n">clk_ss_it</span> <span class="o">=</span> <span class="n">sp</span><span class="o">.</span><span class="n">quantize</span><span class="p">(</span><span class="n">sp</span><span class="o">.</span><span class="n">noisify</span><span class="p">(</span><span class="n">sp</span><span class="o">.</span><span class="n">amplify</span><span class="p">(</span><span class="n">sp</span><span class="o">.</span><span class="n">synth_wave</span><span class="p">(</span><span class="n">clk_it</span><span class="p">,</span> <span class="n">sample_rate</span><span class="p">,</span> <span class="n">rise_time</span><span class="p">),</span> <span class="n">gain</span><span class="o">=</span><span class="mf">10.0</span><span class="p">,</span> <span class="n">offset</span><span class="o">=</span><span class="mf">5.0</span><span class="p">),</span> <span class="n">snr_db</span><span class="o">=</span><span class="mf">20.0</span><span class="p">),</span> <span class="n">full_scale</span><span class="o">=</span><span class="mf">10.0</span><span class="p">)</span>

<span class="c"># Consume iterator and perform all previous operations</span>
<span class="n">clk_samples</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">clk_ss_it</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>


          </div>
        </div>
      </div>
      <div class="sphinxsidebar">
        <div class="sphinxsidebarwrapper">
            <p class="logo"><a href="../index.html">
              <img class="logo" src="../_static/Ripyl logo 100px.png" alt="Logo"/>
            </a></p>
  <h3><a href="../index.html">Table Of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">Common data structures</a><ul>
<li><a class="reference internal" href="#streams">Streams</a><ul>
<li><a class="reference internal" href="#sample-streams">Sample streams</a></li>
<li><a class="reference internal" href="#edge-streams">Edge streams</a></li>
</ul>
</li>
<li><a class="reference internal" href="#streamrecords">StreamRecords</a><ul>
<li><a class="reference internal" href="#annotation">Annotation</a></li>
<li><a class="reference internal" href="#streamrecord-subclasses">StreamRecord subclasses</a></li>
</ul>
</li>
<li><a class="reference internal" href="#iterators">Iterators</a></li>
</ul>
</li>
</ul>

  <h4>Previous topic</h4>
  <p class="topless"><a href="reading_data.html"
                        title="previous chapter">Reading oscilloscope data</a></p>
  <h4>Next topic</h4>
  <p class="topless"><a href="protocols.html"
                        title="next chapter">Ripyl protocols</a></p>
  <h3>This Page</h3>
  <ul class="this-page-menu">
    <li><a href="../_sources/rst/data_structures.txt"
           rel="nofollow">Show Source</a></li>
  </ul>
<div id="searchbox" style="display: none">
  <h3>Quick search</h3>
    <form class="search" action="../search.html" method="get">
      <input type="text" name="q" />
      <input type="submit" value="Go" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
    <p class="searchtip" style="font-size: 90%">
    Enter search terms or a module, class or function name.
    </p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
      <div class="clearer"></div>
    </div>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="../genindex.html" title="General Index"
             >index</a></li>
        <li class="right" >
          <a href="../py-modindex.html" title="Python Module Index"
             >modules</a> |</li>
        <li class="right" >
          <a href="protocols.html" title="Ripyl protocols"
             >next</a> |</li>
        <li class="right" >
          <a href="reading_data.html" title="Reading oscilloscope data"
             >previous</a> |</li>
        <li><a href="../index.html">Ripyl 1.2 documentation</a> &raquo;</li> 
      </ul>
    </div>

    <div class="footer">
        &copy; Copyright 2013, Kevin Thibedeau.
      Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3.
    </div>

<script>
      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
            (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
    })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-43149311-1', 'google.com');
  ga('send', 'pageview');

</script>

  </body>
</html>